/*
 * Copyright (C) 2007 adrian_007, adrian-007 on o2 point pl
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "stdafx.h"
#include "MediaPlayersLib.h"
#include "Util.h"

#include "Players.h"
#include "iTunesCOMInterface.h"
#include "WMPlayerRemoteApi.h"

#include <strmif.h>
#include <control.h>

#define MSN_MAGIC_NUMBER 0x0547

string MediaPlayersLib::msnData;
HWND MediaPlayersLib::msnWnd = NULL;

const TCHAR MediaPlayersLib::itunesHelp[] =
	_T("Apple iTunes media spam\r\n")
	_T("- %[version]\tNumerical Version (ex: 7.0.2.16)\r\n")
	_T("- %[album]\tName of the album the current track is from\r\n")
	_T("- %[artist]\tArtist of the current track\r\n")
	_T("- %[title]\t\tName of the currently playing track\r\n")
	_T("- %[genre]\tGenre of the currently playing track\r\n")
	_T("- %[elapsed]\tTime elapsed in minutes:seconds\r\n")
	_T("- %[percent]\tTime elapsed presented as percentage from total length\r\n")
	_T("- %[bar]\t\tASCII progress bar (ex. [----|-----])\r\n")
	_T("- %[length]\tTotal length of the current track\r\n")
	_T("- %[bitrate]\tBitrate for the playing track (kbps)\r\n")
	_T("- %[frequency]\tFrequency for the currently playing track (kHz)\r\n")
	_T("- %[year]\tYear of publication/recording of the current track\r\n")
	_T("- %[size]\t\tSize of the currently playing file\r\n")
	_T("- %[state]\tCurrent state of iTunes (playing/stopped)");

const TCHAR MediaPlayersLib::wmpHelp[] =
	_T("Windows Media Player media spam - Works with Windows Media Player 9+\r\n")
	_T("- %[version]\tVersion of Windows Media Player\r\n")
	_T("- %[fullversion]\tFull version of Windows Media Player\r\n")
	_T("- %[status]\tPre-formatted status string from Windows Media Player\r\n")
	_T("- %[title]\t\tName of the currently playing item\r\n")
	_T("- %[elapsed]\tTime elapsed in (HH:)MM:SS\r\n")
	_T("- %[percent]\tTime elapsed presented as percentage from total length\r\n")
	_T("- %[bar]\t\tASCII progress bar (ex. [----|-----])\r\n")
	_T("- %[length]\tFull length of the current item in (HH:)MM:SS\r\n")
	_T("- %[artist]\tName of the artist for the currently playing item\r\n")
	_T("- %[album]\tName of the album the current song is from\r\n")
	_T("- %[genre]\tGenre of the current media\r\n")
	_T("- %[bitrate]\tBitrate for the playing media\r\n")
	_T("- %[year]\tYear of album publication (no WMP9 support)\r\n")
	_T("- %[size]\t\tSize of the currently playing file\r\n")
	_T("- %[rating]\tUsers rating, represented with *'s (if available)");

const TCHAR MediaPlayersLib::mpcHelp[] =
	_T("Media Player Classic media spamming (no support on Vista +)\r\n")
	_T("- %[filename]\tName of the file currently playing\r\n")
	_T("- %[title]\tName of the currently playing item\r\n")
	_T("- %[size]\t\tSize of the file currently playing\r\n")
	_T("- %[state]\tCurrent state of Media Player Classic (playing/paused/stopped)\r\n")
	_T("- %[elapsed]\tTime elapsed in minutes:seconds\r\n")
	_T("- %[percent]\tTime elapsed presented as percentage from total length\r\n")
	_T("- %[bar]\t\tASCII progress bar (ex. [----|-----])\r\n")
	_T("- %[length]\tTotal length of the current file");

const TCHAR MediaPlayersLib::msnHelp[] =
	_T("MSN \"Now playing\" emulation\r\n")
	_T("- %[format]\tPreformatted string (display same as MSN)\r\n")
	_T("- %[title]\tName of the currently playing item\r\n")
	_T("- %[artist]\tName of the artist for the currently playing item\r\n")
	_T("- %[album]\tName of the album the current song is from\r\n")
	_T("- %[player]\tThe player used\r\n")
	_T("\r\nNote: parameters other than %[format] and %[player] are not static, so they might get mixed up");

// These procedures catch the now playing notification from WM_COPYDATA
LRESULT CALLBACK MSNMockProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	if(uMsg == WM_COPYDATA) {
		COPYDATASTRUCT* cds = (COPYDATASTRUCT*)lParam;
		if(cds->dwData == MSN_MAGIC_NUMBER) {
			MediaPlayersLib::putMsnData(cds);
			return TRUE;
		}
	}
	
	return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

void MediaPlayersLib::createMSNGhost() {
	if(!msnWnd) {
		// Let's impersonate MSN Messenger
		WNDCLASSEX MSNClass = {
			sizeof(WNDCLASSEX),
			0, MSNMockProc,
			0, 0, GetModuleHandle(NULL),
			NULL, NULL, NULL, NULL, 
			_T("MsnMsgrUIManager"), NULL
		};

		ATOM ghostClass = RegisterClassEx(&MSNClass);

		// Message only window would be ideal but they are not accessible to
		// FindWindow(Ex) except under certain limited conditions.

		if(ghostClass) {
			msnWnd = CreateWindowEx(0, (LPCTSTR)MAKELONG(ghostClass, 0), _T("MSN GhostWindow/MediaPlayersLib"),
				0, 0, 0, 0, 0, NULL, NULL, GetModuleHandle(NULL), NULL);
		}
	} else {
		DestroyWindow(msnWnd);
		msnWnd = NULL;
	}
}

void MediaPlayersLib::putMsnData(const COPYDATASTRUCT* aCds) {
	wstring tmpData(aCds->cbData, L'\0');
	memcpy(&tmpData[0], aCds->lpData, tmpData.size());
	msnData = Util::convertFromWide(tmpData);
}

void MediaPlayersLib::playerControl(const string& sPlayer, int iAction) {
	if(strcmp(sPlayer.c_str(), "winamp") == 0) {
		HWND hwndWinamp = FindWindow(_T("Winamp v1.x"), NULL);
		if(!hwndWinamp) return;
		switch(iAction) {
			case 0: SendMessage(hwndWinamp, WM_COMMAND, WINAMP_BUTTON1, 0); break;
			case 1: SendMessage(hwndWinamp, WM_COMMAND, WINAMP_BUTTON2, 0); break;
			case 2: SendMessage(hwndWinamp, WM_COMMAND, WINAMP_BUTTON3, 0); break;
			case 3: SendMessage(hwndWinamp, WM_COMMAND, WINAMP_BUTTON5, 0); break;
			case 4: SendMessage(hwndWinamp, WM_COMMAND, WINAMP_BUTTON4, 0); break;
			case 5: SendMessage(hwndWinamp, WM_COMMAND, WINAMP_VOLUMEUP, 0); break;
			case 6: SendMessage(hwndWinamp, WM_WA_IPC, 255/2, IPC_SETVOLUME); break;
			case 7: SendMessage(hwndWinamp, WM_COMMAND, WINAMP_VOLUMEDOWN, 0); break;
		}
	} else if(strcmp(sPlayer.c_str(), "wmp") == 0) {
		HWND hwndWMP = FindWindow(_T("WMPlayerApp"), NULL);
		if(!hwndWMP) return;
		switch(iAction) {
			case 0: SendMessage(hwndWMP, WM_COMMAND, WMP_PREV, 0); break;
			case 1: SendMessage(hwndWMP, WM_COMMAND, WMP_ALT_PLAY, 0); break;
			case 2: SendMessage(hwndWMP, WM_COMMAND, WMP_ALT_PLAY, 0); break;
			case 3: SendMessage(hwndWMP, WM_COMMAND, WMP_NEXT, 0); break;
			case 4: SendMessage(hwndWMP, WM_COMMAND, WMP_STOP, 0); break;
			case 5: SendMessage(hwndWMP, WM_COMMAND, WMP_VOLUP, 0); break;
			case 6: SendMessage(hwndWMP, WM_COMMAND, WMP_MUTE, 0); break;
			case 7: SendMessage(hwndWMP, WM_COMMAND, WMP_VOLDOWN, 0); break;
		}
	} else if(strcmp(sPlayer.c_str(), "mpc") == 0) {
		HWND hwndMPC = FindWindow(_T("MediaPlayerClassicW"), NULL);
		if(!hwndMPC) return;
		switch(iAction) {
			case 0: SendMessage(hwndMPC, WM_COMMAND, MPC_PREV, 0); break;
			case 1: SendMessage(hwndMPC, WM_COMMAND, MPC_PLAY, 0); break;
			case 2: SendMessage(hwndMPC, WM_COMMAND, MPC_PAUSE, 0); break;
			case 3: SendMessage(hwndMPC, WM_COMMAND, MPC_NEXT, 0); break;
			case 4: SendMessage(hwndMPC, WM_COMMAND, MPC_STOP, 0); break;
			case 5: SendMessage(hwndMPC, WM_COMMAND, MPC_VOLUP, 0); break;
			case 6: SendMessage(hwndMPC, WM_COMMAND, MPC_MUTE, 0); break;
			case 7: SendMessage(hwndMPC, WM_COMMAND, MPC_VOLDOWN, 0); break;
		}
	} else if(strcmp(sPlayer.c_str(), "itunes") == 0) {
		if(!FindWindow(_T("iTunes"), _T("iTunes"))) return;
		IiTunes *iITunes;
		if(SUCCEEDED(CoCreateInstance(CLSID_iTunesApp, NULL, CLSCTX_LOCAL_SERVER, IID_IiTunes, (PVOID *)&iITunes))) {
			long currVol;
			switch(iAction) {
				case 0: iITunes->PreviousTrack(); break;
				case 1: iITunes->Play(); break;
				case 2: iITunes->Pause(); break;
				case 3: iITunes->NextTrack(); break;
				case 4: iITunes->Stop(); break;
				case 5: iITunes->get_SoundVolume(&currVol); iITunes->put_SoundVolume(currVol+10); break;
				case 6: iITunes->put_SoundVolume(50); break;
				case 7: iITunes->get_SoundVolume(&currVol); iITunes->put_SoundVolume(currVol-10); break;
			}
		}
	}
}

// This is GPLed, and copyright (mostly) my anonymous friend
// - Todd Pederzani
string MediaPlayersLib::getItunesSpam(const string& format, bool& status) {
	// If it's not running don't even bother...
	if(FindWindow(_T("iTunes"), _T("iTunes")) != NULL) {
		// Pointer
		IiTunes *iITunes;

		// Others
		StringMap params;

		// note - CLSID_iTunesApp and IID_IiTunes are defined in iTunesCOMInterface_i.c
		//Create an instance of the top-level object.  iITunes is an interface pointer to IiTunes.  (weird capitalization, but that's how Apple did it)
		if (SUCCEEDED(::CoCreateInstance(CLSID_iTunesApp, NULL, CLSCTX_LOCAL_SERVER, IID_IiTunes, (PVOID *)&iITunes))) {
			long length(0), elapsed;

			//pTrack is a pointer to the track.  This gets passed to other functions to get track data.  wasTrack lets you check if the track was grabbed.
			IITTrack *pTrack;
			//Sanity check -- should never fail if CoCreateInstance succeeded.  You may want to use this for debug output if it does ever fail.
			if (SUCCEEDED(iITunes->get_CurrentTrack(&pTrack)) && pTrack != NULL) {
				//Get album, then call ::COLE2T() to convert the text to array
				CComBSTR album;
				pTrack->get_Album(&album);
				if (album != NULL) {
					COLE2T Album(album);
					params["album"] = Util::convertFromWide(Album.m_szBuffer);
				}

				//Same for artist
				CComBSTR artist;
				pTrack->get_Artist(&artist);
				if(artist != NULL) {
					COLE2T Artist(artist);
					params["artist"] = Util::convertFromWide(Artist.m_szBuffer);
				}

				//Track name (get_Name is inherited from IITObject, of which IITTrack is derived)
				CComBSTR name;
				pTrack->get_Name(&name);
				if(name != NULL) {
					COLE2T Name(name);
					params["title"] = Util::convertFromWide(Name.m_szBuffer);
				}

				// Genre
				CComBSTR genre;
				pTrack->get_Genre(&genre);
				if(genre != NULL) {
					COLE2T Genre(name);
					params["genre"] = Util::convertFromWide(Genre.m_szBuffer);
				}

				//Total song time
				pTrack->get_Duration(&length);
				if (length > 0) { params["length"] = Util::formatSeconds(length); } // <--- once more with feeling

				//Bitrate
				long bitrate;
				pTrack->get_BitRate(&bitrate);
				if (bitrate > 0) { params["bitrate"] = Util::toString(bitrate) + "kbps"; } //<--- I'm not gonna play those games.  Mind games.  Board games.  I'm like, come on fhqugads...

				//Frequency
				long frequency;
				pTrack->get_SampleRate(&frequency);
				if (frequency > 0) { params["frequency"] = Util::toString(frequency/1000) + "kHz"; }

				//Year
				long year;
				pTrack->get_Year(&year);
				if (year > 0) { params["year"] = Util::toString(year); }
			
				//Size
				long size;
				pTrack->get_Size(&size);
				if (size > 0) { params["size"] = Util::formatBytes(size); }

				//Release (decrement reference count to 0) track object so it can unload and free itself; otherwise, it's locked in memory.
				pTrack->Release();
			}

			//Player status (stopped, playing, FF, rewind)
			int state(0);
			ITPlayerState pStatus;
			iITunes->get_PlayerState(&pStatus);
			if (pStatus == ITPlayerStateStopped) {
				params["state"] = "stopped";
				state = 1;
			} else if (pStatus == ITPlayerStatePlaying) {
				params["state"] = "playing";
			}

			//Player position (in seconds, you'll want to convert for your output)
			iITunes->get_PlayerPosition(&elapsed);
			if(elapsed > 0) {
				params["elapsed"] = Util::formatSeconds(elapsed);
				int intPercent;
				if (length > 0 ) {
					intPercent = elapsed * 100 / length;
				} else {
					length = 0;
					intPercent = 0;
				}
				params["percent"] = Util::toString(intPercent) + "%";
				int numFront = min(max(intPercent / 10, 0), 10),
					numBack = min(max(10 - 1 - numFront, 0), 10);
				string inFront = string(numFront, '-'),
				   inBack = string(numBack, '-');
				params["bar"] = "[" + inFront + (elapsed > 0 ? "|" : "-") + inBack + "]";
			}

			//iTunes version
			CComBSTR version;
			iITunes->get_Version(&version);
			if(version != NULL) {
				COLE2T iVersion(version);
				params["version"] = Util::convertFromWide(iVersion.m_szBuffer);
			}

			//Release (decrement reference counter to 0) IiTunes object so it can unload and free itself; otherwise, it's locked in memory
			iITunes->Release();
		}

		// If there is something in title, we have at least partly succeeded 
		if(!params["title"].empty()) {
			return Util::formatParams(format, params);
		} else {
			status = true;
			return "Nothing is currently playing";
		}
	} else {
		status = true;
		return "Supported version of iTunes is not running";
	}
}

// This works for WMP 9+, but it's little slow, but hey we're talking about an MS app here
// so what can you expect from the remote support for it...
string MediaPlayersLib::getWMPSpam(const string& format, bool& status, HWND parentWnd /*= NULL*/) {
	// If it's not running don't even bother...
	if(FindWindow(_T("WMPlayerApp"), NULL) != NULL) {
		// Pointers 
		CComPtr<IWMPPlayer>				Player;
		CComPtr<IAxWinHostWindow>		Host;
		CComPtr<IObjectWithSite>		HostObj;
		CComObject<WMPlayerRemoteApi>*	WMPlayerRemoteApiCtrl = NULL;

		// Other
		HRESULT							hresult;
		CAxWindow*						DummyWnd;
		StringMap						params;

		// Create hidden window to host the control (if there just was other way to do this... as CoCreateInstance has no access to the current running instance)
		AtlAxWinInit();
		DummyWnd = new CAxWindow();
		hresult = DummyWnd? S_OK : E_OUTOFMEMORY;

		if(SUCCEEDED(hresult)) {
			DummyWnd->Create(!parentWnd ? GetActiveWindow() : parentWnd, NULL, NULL, WS_CHILD | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU);
			hresult = ::IsWindow(DummyWnd->m_hWnd)? S_OK : E_FAIL;
		}
		
		// Set WMPlayerRemoteApi
		if(SUCCEEDED(hresult)) {
			hresult = DummyWnd->QueryHost(IID_IObjectWithSite, (void **)&HostObj);
			hresult = HostObj.p? hresult : E_FAIL;

			if(SUCCEEDED(hresult)) {
				hresult = CComObject<WMPlayerRemoteApi>::CreateInstance(&WMPlayerRemoteApiCtrl);
				if(WMPlayerRemoteApiCtrl) {
					WMPlayerRemoteApiCtrl->AddRef();
				} else {
					hresult = E_POINTER;
				}
			}

			if(SUCCEEDED(hresult)) {
				hresult = HostObj->SetSite((IWMPRemoteMediaServices *)WMPlayerRemoteApiCtrl);
			}
		}
		
		if(SUCCEEDED(hresult)) {
			hresult = DummyWnd->QueryHost(&Host);
			hresult = Host.p ? hresult : E_FAIL;
		}

		// Create WMP Control 
		if(SUCCEEDED(hresult)) {
			hresult = Host->CreateControl(CComBSTR(L"{6BF52A52-394A-11d3-B153-00C04F79FAA6}"), DummyWnd->m_hWnd, 0);
		}
	
		// Now we can finally start to interact with WMP, after we successfully get the "Player"
		if(SUCCEEDED(hresult)) {
			hresult = DummyWnd->QueryControl(&Player);
			hresult = Player.p? hresult : E_FAIL;
		}

		// We've got this far now the grande finale, get the metadata :)
		if(SUCCEEDED(hresult)) {
			CComPtr<IWMPMedia>		Media;
			CComPtr<IWMPControls>	Controls;

			if(SUCCEEDED(Player->get_currentMedia(&Media)) && Media.p != NULL) {
				Player->get_controls(&Controls);

				// Windows Media Player version
				CComBSTR bstrWMPVer;
				Player->get_versionInfo(&bstrWMPVer);
				if(bstrWMPVer != NULL) {
					COLE2T WMPVer(bstrWMPVer);
					params["fullversion"] = Util::convertFromWide(WMPVer.m_szBuffer);
					params["version"] = params["fullversion"].substr(0, params["fullversion"].find("."));
				}

				// Pre-formatted status message from Windows Media Player
				CComBSTR bstrWMPStatus;
				Player->get_status(&bstrWMPStatus);
				if(bstrWMPStatus != NULL) {
					COLE2T WMPStatus(bstrWMPStatus);
					params["status"] = Util::convertFromWide(WMPStatus.m_szBuffer);
				}

				// Name of the currently playing media
				CComBSTR bstrMediaName;
				Media->get_name(&bstrMediaName);
				if(bstrMediaName != NULL) {
					COLE2T MediaName(bstrMediaName);
					params["title"] = Util::convertFromWide(MediaName.m_szBuffer);
				}

				// How much the user has already played 
				// I know this is later duplicated with get_currentPosition() for percent and bar, but for some reason it's overall faster this way 
				CComBSTR bstrMediaPosition;
				Controls->get_currentPositionString(&bstrMediaPosition);
				if(bstrMediaPosition != NULL) {
					COLE2T MediaPosition(bstrMediaPosition);
					params["elapsed"] = Util::convertFromWide(MediaPosition.m_szBuffer);
				}

				// Full duratiuon of the media
				// I know this is later duplicated with get_duration() for percent and bar, but for some reason it's overall faster this way 
				CComBSTR bstrMediaDuration;
				Media->get_durationString(&bstrMediaDuration);
				if(bstrMediaDuration != NULL) {
					COLE2T MediaDuration(bstrMediaDuration);
					params["length"] = Util::convertFromWide(MediaDuration.m_szBuffer);
				}

				// Name of the artist (use Author as secondary choice)
				CComBSTR bstrArtistName;
				Media->getItemInfo(CComBSTR(_T("WM/AlbumArtist")), &bstrArtistName);
				if(bstrArtistName != NULL) {
					COLE2T ArtistName(bstrArtistName);
					params["artist"] = Util::convertFromWide(ArtistName.m_szBuffer);
				} else {
					Media->getItemInfo(CComBSTR(_T("Author")), &bstrArtistName);
					if(bstrArtistName != NULL) {
						COLE2T ArtistName(bstrArtistName);
						params["artist"] = Util::convertFromWide(ArtistName.m_szBuffer);
					}
				}

				// Name of the album
				CComBSTR bstrAlbumTitle;
				Media->getItemInfo(CComBSTR(_T("WM/AlbumTitle")), &bstrAlbumTitle);
				if(bstrAlbumTitle != NULL) {
					COLE2T AlbumName(bstrAlbumTitle);
					params["album"] = Util::convertFromWide(AlbumName.m_szBuffer);
				}

				// Genre of the media
				CComBSTR bstrMediaGen;
				Media->getItemInfo(CComBSTR(_T("WM/Genre")), &bstrMediaGen);
				if(bstrMediaGen != NULL) {
					COLE2T MediaGen(bstrMediaGen);
					params["genre"] = Util::convertFromWide(MediaGen.m_szBuffer);
				}

				// Year of publiciation
				CComBSTR bstrMediaYear;
				Media->getItemInfo(CComBSTR(_T("WM/Year")), &bstrMediaYear);
				if(bstrMediaYear != NULL) {
					COLE2T MediaYear(bstrMediaYear);
					params["year"] = Util::convertFromWide(MediaYear.m_szBuffer);
				} else {
					Media->getItemInfo(CComBSTR(_T("ReleaseDateYear")), &bstrMediaYear);
					if(bstrMediaYear != NULL) {
						COLE2T MediaYear(bstrMediaYear);
						params["year"] = Util::convertFromWide(MediaYear.m_szBuffer);
					}
				}

				// Bitrate, displayed as Windows Media Player displays it
				CComBSTR bstrMediaBitrate;
				Media->getItemInfo(CComBSTR(_T("Bitrate")), &bstrMediaBitrate);
				if(bstrMediaBitrate != NULL) {
					COLE2T MediaBitrate(bstrMediaBitrate);
					double BitrateAsKbps = (Util::toDouble(Util::convertFromWide(MediaBitrate.m_szBuffer))/1000);
					params["bitrate"] = Util::toString(int(BitrateAsKbps)) + "kbps";
				}

				// Size of the file
				CComBSTR bstrMediaSize;
				Media->getItemInfo(CComBSTR(_T("Size")), &bstrMediaSize);
				if(bstrMediaSize != NULL) {
					COLE2T MediaSize(bstrMediaSize);
					params["size"] = Util::formatBytes(_atoi64(Util::convertFromWide(MediaSize.m_szBuffer).c_str()));
				}

				// Users rating for this media
				CComBSTR bstrUserRating;
				Media->getItemInfo(CComBSTR(_T("UserRating")), &bstrUserRating);
				if(bstrUserRating != NULL) {
					if(bstrUserRating == "0") {
						params["rating"] = "unrated";
					} else if(bstrUserRating == "1") {
						params["rating"] = "*";
					} else if(bstrUserRating == "25") {
						params["rating"] = "* *";
					} else if(bstrUserRating == "50") {
						params["rating"] = "* * *";
					} else if(bstrUserRating == "75") {
						params["rating"] = "* * * *";
					} else if(bstrUserRating == "99") {
						params["rating"] = "* * * * *";
					} else {
						params["rating"] = "";
					}
				}

				// Bar & percent
				double elapsed;
				double length;
				Controls->get_currentPosition(&elapsed);
				Media->get_duration(&length);
				if(elapsed > 0) {
					int intPercent;
					if (length > 0 ) {
						intPercent = int(elapsed) * 100 / int(length);
					} else {
						length = 0;
						intPercent = 0;
					}
					params["percent"] = Util::toString(intPercent) + "%";
					int numFront = min(max(intPercent / 10, 0), 10),
						numBack = min(max(10 - 1 - numFront, 0), 10);
					string inFront = string(numFront, '-'),
						inBack = string(numBack, '-');
					params["bar"] = "[" + inFront + (elapsed > 0 ? "|" : "-") + inBack + "]";
				} else {
					params["percent"] = "0%";
					params["bar"] = "[|---------]";
				}
			}
		}

		// Release WMPlayerRemoteApi, if it's there
		if(WMPlayerRemoteApiCtrl) {
			WMPlayerRemoteApiCtrl->Release();
		}
			
		// Destroy the hoster window, and unload COM
		DummyWnd->DestroyWindow();
		delete DummyWnd;
		AtlAxWinTerm();
			
		// If there is something in title, we have at least partly succeeded 
		if(!params["title"].empty()) {
			return Util::formatParams(format, params);
		} else {
			status = true;
			return "Nothing is currently playing";
		}
	} else {
		status = true;
		return "Supported version of Windows Media Player is not running";
	}
}

// mpc = mplayerc = MediaPlayer Classic
// liberally inspired (copied with changes) by the GPLed application mpcinfo by Gabest
// Note: No longer works with Vista and above, thanks to Microsofts infinite wisdom!
string MediaPlayersLib::getMPCSpam(const string& format, bool& status) {
	StringMap params;
	bool success = false;
	CComPtr<IRunningObjectTable> pROT;
	CComPtr<IEnumMoniker> pEM;
	CComQIPtr<IFilterGraph> pFG;
	if(GetRunningObjectTable(0, &pROT) == S_OK && pROT->EnumRunning(&pEM) == S_OK) {
		CComPtr<IBindCtx> pBindCtx;
		CreateBindCtx(0, &pBindCtx);
		for(CComPtr<IMoniker> pMoniker; pEM->Next(1, &pMoniker, NULL) == S_OK; pMoniker = NULL) {
			LPOLESTR pDispName = NULL;
			if(pMoniker->GetDisplayName(pBindCtx, NULL, &pDispName) != S_OK)
				continue;
			wstring strw(pDispName);
			CComPtr<IMalloc> pMalloc;
			if(CoGetMalloc(1, &pMalloc) != S_OK)
				continue;
			pMalloc->Free(pDispName);
			// Prefix string literals with the L character to indicate a UNCODE string.
			if(strw.find(L"(MPC)") == wstring::npos)
				continue;
			CComPtr<IUnknown> pUnk;
			if(pROT->GetObject(pMoniker, &pUnk) != S_OK)
				continue;
			pFG = pUnk;
			if(!pFG)
				continue;
			success = true;
			break;
		}

		if (success) {
			// file routine (contains size routine)
			CComPtr<IEnumFilters> pEF;
			if(pFG->EnumFilters(&pEF) == S_OK) {
				// from the file routine
				ULONG cFetched = 0;
				for(CComPtr<IBaseFilter> pBF; pEF->Next(1, &pBF, &cFetched) == S_OK; pBF = NULL) {
					if(CComQIPtr<IFileSourceFilter> pFSF = pBF) {
						LPOLESTR pFileName = NULL;
						AM_MEDIA_TYPE mt;
						if(pFSF->GetCurFile(&pFileName, &mt) == S_OK) {
							// second parameter is a AM_MEDIA_TYPE structure, which contains codec info
							params["filename"] = Util::getFileName(Util::convertFromWide(pFileName)); //otherwise fully qualified
							params["title"] = params["filename"].substr(0, params["filename"].size() - 4);
							params["size"] = Util::formatBytes(Util::getFileSize(pFileName));
							CoTaskMemFree(pFileName);
							// alternative to FreeMediaType(mt)
							// provided by MSDN DirectX 9 help page for FreeMediaType
							if (mt.cbFormat != 0)
							{
								CoTaskMemFree((PVOID)mt.pbFormat);
								mt.cbFormat = 0;
								mt.pbFormat = NULL;
							}
							if (mt.pUnk != NULL)
							{
								// Unecessary because pUnk should not be used, but safest.
								mt.pUnk->Release();
								mt.pUnk = NULL;
							}
							// end provided by MSDN
							break;
						}
					}
				}
			}

			// paused / stopped / running?
			CComQIPtr<IMediaControl> pMC;
			OAFilterState fs;
			int state = 0;
			if((pMC = pFG) && (pMC->GetState(0, &fs) == S_OK)) {
				switch(fs) {
					case State_Running:
						params["state"] = "playing";
						state = 1;
						break;
					case State_Paused:
						params["state"] = "paused";
						state = 3;
						break;
					case State_Stopped:
						params["state"] = "stopped";
						state = 0;
				};
			}

			// position routine
			CComQIPtr<IMediaSeeking> pMS = pFG;
			REFERENCE_TIME pos, dur;
			if((pMS->GetCurrentPosition(&pos) == S_OK) && (pMS->GetDuration(&dur) == S_OK)) {
				params["elapsed"] = Util::formatSeconds(pos/10000000);
				params["length"] = Util::formatSeconds(dur/10000000);
				int intPercent = 0;
				if (dur != 0)
					intPercent = (int) (pos * 100 / dur);
				params["percent"] = Util::toString(intPercent) + "%";
				int numFront = min(max(intPercent / 10, 0), 10),
					numBack = min(max(10 - 1 - numFront, 0), 10);
				string inFront = string(numFront, '-'),
					   inBack = string(numBack, '-');
				params["bar"] = "[" + inFront + (state ? "|" : "-") + inBack + "]";
			}
			/*
			*	"+me watches: %[filename] (%[elapsed]/%[length]) Size: %[size]"
			*	from mpcinfo.txt
			*	"+me is playing %[filename] %[size] (%[elapsed]/%[length]) Media Player Classic %[version]"
			*	from http://www.faireal.net/articles/6/25/
			*/
		}
	}

	if (success) {
		return Util::formatParams(format, params);
	} else {
		status = true;
		return "Supported version of Media Player Classic is not running";
	}
}

string MediaPlayersLib::getMSNSpam(const string& format, bool& status) {
	if(msnData.empty()) {
		status = true;
		return "No info about playing item available";
	}

	StringList parts;
	Util::tokenizeString(msnData, "\\0", parts);

	if(parts[2] == "0") {
		status = true;
		return "Nothing currently playing";
	}

	StringMap params;
	size_t pos = 0;
	for(StringIter i = parts.begin(); i != parts.end(); ++i, ++pos) {
		switch(pos) {
			case 0: if(!i->empty()) params["player"] = *i; break;
			case 3: if(!i->empty()) params["format"] = *i; break;
			case 4: if(!i->empty()) params["artist"] = *i; break;
			case 5: if(!i->empty()) params["title"] = *i; break;
			case 6: if(!i->empty()) params["album"] = *i; break;
			case 7: if(!i->empty()) params["uuid"] = *i; break;
		}
	}

	if(params.empty() || parts.size() < 5) {
		status = true;
		return "No info about playing item available";
	}

	// Begin the guessing game, the problem:
	// {0} - {1}, artist, title is same as {1} - {0}, title, artist 
	string& msnFormat = params["format"];
	if(parts.size() == 5) {
		string::size_type pos = msnFormat.find(" - ");
		params["artist"] = msnFormat.substr(0, pos);
		params["title"] = msnFormat.substr(pos + 3);
	} else if(parts.size() <= 8)
		std::swap(params["artist"], params["title"]);

	// parse format
	string::size_type i = 0, j;
	while((j = msnFormat.find("{", i)) != string::npos) {
		if(msnFormat.size() < j + 2) break;
		if(msnFormat[j+2] == '}') {
			int idx = atoi(&msnFormat[j+1]) + 4;
			const string& tmp = parts[idx];
			msnFormat.replace(j, 3, tmp);
			i = j + tmp.size();
		} else i = j + 1;
	}

	return Util::formatParams(format, params);
}